﻿using System;
using System.Collections.Generic;
using System.Text;
using AdaptiveAgents.TeammateModels;

namespace AdaptiveAgents.Agents.Choosers
{
    /// <summary>
    /// Chooses next player according to MDP
    /// </summary>
    class MDPDiscoverNextPlayerChooser_old : NextPlayerChooser
    {

        protected const double LAMBDA = 0.9999;
        protected EquationSolver.EquationSolver solver = new EquationSolver.EquationSolver();


        public MDPDiscoverNextPlayerChooser_old(Agent agent)
            : base(agent)
        {
            agent.NextPlayerChooser = this;
        }

        /// <summary>
        /// Calculates the probability of a specific agent according to its statistics
        /// </summary>
        /// <param name="fromEpsilon">exploration of the specific agent</param>
        /// <param name="numAgents">total number of players in game</param>
        /// <param name="explotation">predicted explotation of the movement</param>
        /// <returns>probability of the movement</returns>
        protected double calcProbabilty(double fromEpsilon, int numAgents, int explotation)
        {
            double result = fromEpsilon / (numAgents - 1); // Epsilon_i / (N - 1)
            return result + explotation * (1 - fromEpsilon);
        }

        /// <summary>
        /// Creates a new 4 element vector 
        /// </summary>
        /// <param name="v0">element 0</param>
        /// <param name="v1">element 1</param>
        /// <param name="v2">element 2</param>
        /// <param name="v3">element 3</param>
        /// <returns>new created vector contains the specified elements</returns>
        protected double[] setVector(double v0, double v1, double v2, double v3)
        {
            double[] vec = new double[4];
            vec[0] = v0;
            vec[1] = v1;
            vec[2] = v2;
            vec[3] = v3;

            return vec;
        }

        /// <summary>
        /// Chooses the index of the agent with max performence
        /// </summary>
        /// <param name="value0">utility of first agent</param>
        /// <param name="index0">index of first agent</param>
        /// <param name="value1">utility of second agent</param>
        /// <param name="index1">index of second agent</param>
        /// <param name="valueS">utility of strategic agent</param>
        /// <param name="indexS">index of strategic agent</param>
        /// <returns>index of the agent with max utility</returns>
        protected int chooseMaxValue(double value0, int index0, double value1, int index1, double valueS, int indexS)
        {
            if (value0 > value1)
            {
                if (value0 > valueS)
                    return index0;
                else if (value0 == valueS)
                {
                    if (Generator.getNextDouble() < 0.5)
                        return index0;
                    return indexS;
                }
                else return indexS;
            }
            else if (value0 == value1)
            {
                if (valueS > value1)
                    return indexS;
                else if (valueS == value1)
                {
                    switch (Generator.getNextInt(3)) //tie //HACK//random 0,1,s
                    {
                        case 0:
                            return index0;
                        case 1:
                            return index1;
                        case 2:
                            return indexS;
                    }
                }
                if (Generator.getNextDouble() < 0.5) //tie
                    return index0;
                return index1;
            }
            else if (value1 > valueS)
                return index1;
            else if (valueS == value1)
            {
                if (Generator.getNextDouble() < 0.5) //tie
                    return index1;
                return indexS;
            }

            return indexS;
        }

        /// <summary>
        /// Chooses the next performer according to MDP
        /// </summary>
        /// <param name="maxToConsider"></param>
        /// <returns>index of next performer</returns>
        public override int choose(int maxToConsider)
        {
            if (_me.ObservationsSet.observations.Count < _me.ChangeStrategyPoint) //HACK: Observe before decide about the MDP
                // if (_me.getObservations().observations.Count < 180)
                return Generator.getNextInt(3);

            int agentS, agent0, agent1;
            int numOfAgents = 3;

            agentS = _me.ID;
            agent0 = (agentS + 1) % 3;
            agent1 = (agentS + 2) % 3;

            double epsS = _me.Epsilon;
            double compS = _me.Competence.Mean;
            double compSHat = _me.getTeammateModel(agentS).EstimateCompetence();

            double[] data = ((AdaptiveTeammateModel_old)_me.getTeammateModel(agent0)).estimateEpsilonAndCometence(_me.ObservationsSet.observations);

            double eps0 = data[0];
            double comp0 = data[1];
            double comp0Hat = _me.getTeammateModel(agent0).EstimateCompetence();

            data = ((AdaptiveTeammateModel_old)_me.getTeammateModel(agent1)).estimateEpsilonAndCometence(_me.ObservationsSet.observations);

            double eps1 = data[0];
            double comp1 = data[1];
            double comp1Hat = _me.getTeammateModel(agent1).EstimateCompetence();

            double p0to0 = (1 - eps0) * (comp0 >= comp1Hat && comp0 >= compSHat ? 1 : 0);
            double p1to1 = (1 - eps1) * (comp1 >= comp0Hat && comp1 >= compSHat ? 1 : 0);

            int explotation0to1 = comp1Hat >= comp0Hat && comp1Hat >= compSHat && comp1Hat >= comp0 ? 1 : 0;
            int explotation0toS = compSHat >= comp0Hat && compSHat >= comp1Hat && compSHat >= comp0 ? 1 : 0;
            int explotation1to0 = comp0Hat >= comp1Hat && comp0Hat >= compSHat && comp0Hat >= comp1 ? 1 : 0;
            int explotation1toS = compSHat >= comp1Hat && compSHat >= comp0Hat && compSHat >= comp1 ? 1 : 0;

            double p0to1 = calcProbabilty(eps0, numOfAgents, explotation0to1);
            double p0toS = calcProbabilty(eps0, numOfAgents, explotation0toS);
            double p1to0 = calcProbabilty(eps1, numOfAgents, explotation1to0);
            double p1toS = calcProbabilty(eps1, numOfAgents, explotation1toS);

            //double[] vector = new double[4];
            solver.addEquation(setVector(LAMBDA * p0to0 - 1, LAMBDA * p0to1, LAMBDA * p0toS, -comp0));
            solver.addEquation(setVector(LAMBDA * p1to0, LAMBDA * p1to1 - 1, LAMBDA * p1toS, -comp1));
            solver.addEquation(setVector(LAMBDA, 0, -1, -compS));

            double[] result = solver.solve();
            double valueAgentSto0 = result[2];
            solver.clear();

            solver.addEquation(setVector(LAMBDA * p0to0 - 1, LAMBDA * p0to1, LAMBDA * p0toS, -comp0));
            solver.addEquation(setVector(LAMBDA * p1to0, LAMBDA * p1to1 - 1, LAMBDA * p1toS, -comp1));
            solver.addEquation(setVector(0, LAMBDA, -1, -compS));

            result = solver.solve();
            double valueAgentSto1 = result[2];
            solver.clear();

            solver.addEquation(setVector(LAMBDA * p0to0 - 1, LAMBDA * p0to1, LAMBDA * p0toS, -comp0));
            solver.addEquation(setVector(LAMBDA * p1to0, LAMBDA * p1to1 - 1, LAMBDA * p1toS, -comp1));
            solver.addEquation(setVector(0, 0, 1 - LAMBDA, compS));

            result = solver.solve();
            double valueAgentStoS = result[2];
            solver.clear();

            int index = chooseMaxValue(valueAgentSto0, agent0, valueAgentSto1, agent1, valueAgentStoS, agentS);

            return index;
        }
    }
}
